/**
 * @author: mattwang@tencent.com
 * @date: 2012-10-20
 */

#include "svd_core.h"

// load in latest model from model_folder
int SvdCore::sync_latest_model() {
	FILE *fi = NULL, *last = NULL;
	char name[256];
	int s_counter = svd_param.start_counter;
	do {
		if (last != NULL)
			fclose(last);
		last = fi;
		sprintf(name, "%s/%04d.model", svd_param.model_out_folder.c_str(), s_counter++);
		fi = fopen(name, "rb");
	} while (fi != NULL);

	if (last != NULL) {
		svd_trainer = new SvdTrainer();
		svd_trainer->load_model(last);
		svd_param.start_counter = s_counter - 1;
		fclose(last);
		return 1;
	} else {
		return 0;
	}
}

void SvdCore::load_model() {
	FILE *fi = fopen(svd_param.model_in.c_str(), "rb");
	// load model from file
	svd_trainer = new SvdTrainer();
	svd_trainer->load_model(fi);
	fclose(fi);
}

void SvdCore::save_model() {
	char name[256];
	sprintf(name, "%s/%04d.model", svd_param.model_out_folder.c_str(), svd_param.start_counter++);
	FILE *fo = fopen(name, "wb");
	svd_trainer->save_model(fo);
	fclose(fo);
}

void SvdCore::init(void) {
	if (svd_param.continue_training != 0 && sync_latest_model() != 0) {
	} else {
		svd_param.continue_training = 0;
		switch (svd_param.task) {
		case 0:
			svd_trainer = new SvdTrainer();
			svd_trainer->init_model();
			break;
		case 1:
			this->load_model();
			break;
		default:
			info("unknown task");
		}
	}
//	this->configure_iterator();
	svd_trainer->init_trainer();
	init_end = 1;
}

void SvdCore::reset_default() {
	init_end = 0;
	svd_trainer = NULL;
	itr_plus = NULL;
	train_repeat = 1;
	num_round = 10;
}

void SvdCore::update(int r, unsigned long elapsed, time_t start) {

	IDataIterator<ReDataBlock> * itr = itr_plus;
	size_t total_num = itr->get_data_size() * svd_param.train_repeat;

	// exceptional case when iterator didn't provide data count
	if (total_num == 0)
		total_num = 1;

	size_t print_step = static_cast<size_t>(floorf(total_num * svd_param.print_ratio));
	if (print_step <= 0)
		print_step = 1;
	size_t sample_counter = 0;
	ReDataBlock dt;
	int wait_sync_count = 0;
	for (int j = 0; j < svd_param.train_repeat; j++) {
		while (itr->next(dt)) {
			svd_trainer->update(dt);
			wait_sync_count += dt.data.num_row;
			if (wait_sync_count > svd_param.sync_count) {
				svd_trainer->global_sync_model();
				wait_sync_count = 0;
			}
			if (sample_counter % print_step == 0) {
				if (!svd_param.silent) {
					elapsed = (unsigned long) (time(NULL) - start);
					printf("\r                                                                     \r");
					printf("round %8d:[%05.1lf%%] %lu sec elapsed", r, (double) sample_counter / total_num * 100.0,
							elapsed);
					fflush(stdout);
				}
			}
			sample_counter++;
		}
		svd_trainer->global_sync_model();
		itr->before_first();
	}
}

void SvdCore::train(void) {
	this->init();
	if (!svd_param.silent) {
		printf("initializing end, start updating\n");
	}
	time_t start = time(NULL);
	unsigned long elapsed = 0;

	if (svd_param.continue_training == 0) {
		this->save_model();
	}

	int cc = svd_param.max_round;
	while (cc--) {
		this->update(svd_param.start_counter - 1, elapsed, start);

		elapsed = (unsigned long) (time(NULL) - start);
		this->save_model();
	}

	if (!svd_param.silent) {
		info("\nupdating end, %lu sec in all\n", elapsed);
	}
}

